<?php

namespace App\Http\Validate\Article;

use App\Error\BaseError;
use App\Exceptions\KfHttpResponseException;
use App\Http\Validate\AbstractBaseValidate;
use App\Models\Article\ArticleCategoryMappingsModel;
use App\Models\Article\ArticleCategoryModel;
use App\Models\Article\ArticleModel;
use App\Models\Article\ArticleRecommendModel;
use App\Models\Article\ArticleUserRelationModel;
use Illuminate\Support\Facades\Validator;
use Illuminate\Support\MessageBag;
use Illuminate\Validation\Rule;
use Illuminate\Validation\ValidationException;
use Psr\Container\ContainerExceptionInterface;
use Psr\Container\NotFoundExceptionInterface;

class ArticleValidate extends AbstractBaseValidate
{
    function getRules(): array
    {
        // TODO: Implement getRules() method.
        return [];
    }

    function getAttributes(): array
    {
        // TODO: Implement getAttributes() method.
        return [
            "id"         => "内容",
            "previewUrl" => "预览图片"
        ];
    }

    function registerValidateRules(): void
    {
        // TODO: Implement registerValidateRules() method.
        Validator::extend(
            'arrInSize',
            function ($attribute, $value, $parameters, \Illuminate\Validation\Validator $validator) {
                return in_array(count($value), $parameters);
            },
            ':attribute 数组长度必须在 :value 之中'
        );

        Validator::replacer(
            "arrInSize",
            function ($msg, $field, $rule, $params, \Illuminate\Validation\Validator $validator) {
                return str_replace(':value', implode(",", $params), $msg);
            }
        );
    }

    function getMessages(): array
    {
        // TODO: Implement getMessages() method.
        return [
            "id.exists" => "内容已下架或尚未生效"
        ];
    }


    /**
     * @return MessageBag|object
     * @throws ValidationException
     * @throws ContainerExceptionInterface
     * @throws NotFoundExceptionInterface
     */
    public static function addValidate()
    {
        $params = request()->all();
        return self::getInstance()->validate(
            [
                "type",
                "title",
                "subTitle",
                "description",
                "coverImgUrl",
                "previewUrl",
                "content",
                "effectiveTime",
                "publishType",
                "sort",
                "putTop",
                "extra"
            ],
            $params,
            [
                "type"          => self::intInValidateBuild(true, ArticleModel::TYPE_TEXT),
                "title"         => self::stringValidateBuild(true, 120, 1),
                "subTitle"      => self::stringValidateBuild(true, 240, 1),
                "description"   => self::stringValidateBuild(false, 800, 0),
                "coverImgUrl"   => "exclude_unless:type," . ArticleModel::TYPE_ARTICLE . "required|string|url",
                "previewUrl"    => Rule::when(
                    attributeGet($params, "type", null) == ArticleModel::TYPE_VIDEO,
                    "required|array|arrInSize:1",
                    "required|array|arrInSize:1,4"
                ),
                "previewUrl.*"  => [
                    "string",
                    "url"
                ],
                "content"       => Rule::when(
                    attributeGet($params, "type", null) == ArticleModel::TYPE_VIDEO,
                    "required|url",
                    "required|string"
                ),
                "publishType"   => self::intInValidateBuild(true, ArticleModel::PUBLISH_TYPE_TEXT),
                "effectiveTime" => "exclude_unless:publishType," . ArticleModel::PUBLISH_TYPE_TIMING . "|required|date_format:Y-m-d H:i:s",
                "sort"          => "required|int|min:0",
                "putTop"        => self::intInValidateBuild(true, ArticleModel::PUT_TOP_TEXT),
                "extra"         => "required|json"
            ]
        );
    }

    /**
     * 更新校验
     *
     * @return MessageBag|object
     * @throws ValidationException
     * @throws ContainerExceptionInterface
     * @throws NotFoundExceptionInterface
     */
    public static function updateValidate($params)
    {
        $id           = attributeGet($params, "id", 0);
        $articleModel = ArticleModel::query()->where("id", $id)->first();
        if (empty($articleModel)) {
            throw new KfHttpResponseException(BaseError::SERVER_INTERNAL_ERROR);
        }
        $isPublishDraft = $articleModel->publishType == ArticleModel::PUBLISH_TYPE_DRAFT;
        $validateFields = [
            "id",
            "type",
            "saveOnly",
            "title",
            "subTitle",
            "coverImgUrl",
            "description",
            "previewUrl",
            "content",
            "sort",
            "putTop",
            "extra"
        ];
        $validateRules  = [
            "id"           => [
                "required",
                "int"
            ],
            "saveOnly"     => "sometimes|required|in:0,1",
            "title"        => self::stringValidateBuild(true, 120, 1),
            "subTitle"     => self::stringValidateBuild(true, 240, 1),
            "description"  => self::stringValidateBuild(false, 800, 0),
            "type"         => self::intInValidateBuild(true, ArticleModel::TYPE_TEXT),
            "coverImgUrl"  => "exclude_unless:type," . ArticleModel::TYPE_ARTICLE . "required|string|url",
            "previewUrl"   => Rule::when(
                $articleModel->type == ArticleModel::TYPE_VIDEO,
                "required|array|arrInSize:1",
                "required|array|arrInSize:1,4"
            ),
            "previewUrl.*" => [
                "string",
                "url"
            ],
            "content"      => Rule::when(
                $articleModel->type == ArticleModel::TYPE_VIDEO,
                "required|url",
                "required|string"
            ),
            "sort"         => "required|int|min:0",
            "putTop"       => self::intInValidateBuild(true, ArticleModel::PUT_TOP_TEXT),
            "extra"        => "required|json"
        ];

        if (!isset($params["saveOnly"]) || !$params["saveOnly"]) {
            $validateFields = array_merge($validateFields, ["effectiveTime", "publishType"]);
            if ($isPublishDraft) {
                //草稿状态编辑
                $validateRules = array_merge($validateRules, [
                    "publishType"   => self::intInValidateBuild(true, ArticleModel::PUBLISH_TYPE_TEXT),
                    "effectiveTime" => "exclude_unless:publishType," . ArticleModel::PUBLISH_TYPE_TIMING . "|required|date_format:Y-m-d H:i:s"
                ]);
            } else {
                //非草稿状态编辑
                $validateRules = array_merge($validateRules, [
                    "publishType"   => self::intInValidateBuild(true, ArticleModel::PUBLISH_NO_DRAFT_TYPE_TEXT),
                    "effectiveTime" => "exclude_unless:publishType," . ArticleModel::PUBLISH_TYPE_TIMING . "|required|date_format:Y-m-d H:i:s"
                ]);
            }
        }

        return self::getInstance()->validate(
            $validateFields,
            array_merge($params, ["type" => $articleModel->type]),
            $validateRules
        );
    }

    /**
     * 详情校验
     *
     * @return MessageBag|object
     * @throws ValidationException
     * @throws ContainerExceptionInterface
     * @throws NotFoundExceptionInterface
     */
    public static function detailValidate($params)
    {
        return self::getInstance()->validate(
            ["id"],
            $params,
            [
                "id" => [
                    "required",
                    "int",
                    Rule::exists((new ArticleModel())->getTable(), "id")->where("isDeleted", ArticleModel::DELETE_NO)
                ]
            ]
        );
    }


    /**
     * 删除校验
     *
     * @return MessageBag|object
     * @throws ValidationException
     * @throws ContainerExceptionInterface
     * @throws NotFoundExceptionInterface
     */
    public static function delValidate($params)
    {
        return self::getInstance()->validate(
            ["id"],
            $params,
            [
                "id" => [
                    "required",
                    "int",
                    Rule::exists((new ArticleModel())->getTable(), "id"),
                    function ($attribute, $value, $fail) {
                        $exists = ArticleRecommendModel::query()->where("contentType",
                            ArticleRecommendModel::CONTENT_TYPE_ARTICLE)->where("articleId", $value)->first();
                        if ($exists) {
                            $fail(vsprintf("文章已绑定推荐位 [%s] [%s]", [
                                ArticleRecommendModel::TYPE_TEXT[$exists->type] ?? "-",
                                $exists->title
                            ]));
                        }
                    },
                    function ($attribute, $value, $fail) {
                        $exists = ArticleCategoryMappingsModel::query()
                                                              ->where("articleId", $value)
                                                              ->with("articleCategoryRel")
                                                              ->whereHas("articleCategoryRel")
                                                              ->first();

                        if ($exists) {
                            $fail(vsprintf("文章已绑定专题 [专题名称 : %s]", [
                                $exists->articleCategoryRel->name
                            ]));
                        }
                    }
                ]
            ]
        );
    }

    /**
     * 列表校验
     *
     * @param array $params
     * @return MessageBag|object
     * @throws ContainerExceptionInterface
     * @throws NotFoundExceptionInterface
     * @throws ValidationException
     */
    public static function listValidate(array $params)
    {
        return self::getInstance()->validate(
            [
                "pageSize",
                "pageNum",
                "title",
                "type",
                "status",
                "publishStatus",
                "sortField",
                "createStartTime",
                "createEndTime",
                "effectiveStartTime",
                "effectiveEndTime",
                "createdByUid",
                "isDrafts",
                "statisticalStartAt",
                "statisticalEndAt",
                "sortOrder",
                "categoryId"
            ],
            $params,
            [
                "pageSize"           => "required|int|min:1|max:100",
                "pageNum"            => "required|int|min:1",
                "title"              => "sometimes|string|min:1",
                "type"               => "sometimes|in:" . implode(",", array_keys(ArticleModel::TYPE_TEXT)),
                "status"             => "sometimes|in:" . implode(",", array_keys(ArticleModel::STATUS_TEXT)),
                "publishStatus"      => "sometimes|in:" . implode(",",
                        array_keys(ArticleModel::PUBLISH_SELECTION_TYPE_TEXT)),
                "sortField"          => "sometimes|in:supportNum,collectNum,forwardNum,readNum,showNum",
                "sortOrder"          => "sometimes|in:descend,ascend",
                "createStartTime"    => "sometimes|date_format:Y-m-d",
                "createEndTime"      => "sometimes|date_format:Y-m-d",
                "effectiveStartTime" => "sometimes|date_format:Y-m-d",
                "effectiveEndTime"   => "sometimes|date_format:Y-m-d",
                "createdByUid"       => "sometimes|int|min:1",
                "isDrafts"           => "sometimes|int|in:0,1",
                "statisticalStartAt" => "",
                "statisticalEndAt"   => "",
                "categoryId"         => [
                    "sometimes",
                    Rule::exists((new ArticleCategoryModel())->getTable(), "id")->where("isDeleted", 0)
                ]
            ]
        );
    }

    /**
     * 上下架 校验
     *
     * @return MessageBag|object
     * @throws ValidationException
     * @throws ContainerExceptionInterface
     * @throws NotFoundExceptionInterface
     */
    public static function statusValidate($params)
    {
        return self::getInstance()->validate(
            ["id", "status"],
            $params,
            [
                "id"     => [
                    "required",
                    "int",
                    Rule::exists((new ArticleModel())->getTable(), "id")
                ],
                "status" => self::intInValidateBuild(true, ArticleModel::STATUS_TEXT)
            ]
        );
    }

    /**
     * 置顶校验
     *
     * @param $params
     * @return MessageBag|object
     * @throws ContainerExceptionInterface
     * @throws NotFoundExceptionInterface
     * @throws ValidationException
     */
    public static function putTopValidate($params)
    {
        return self::getInstance()->validate(
            ["id", "putTop"],
            $params,
            [
                "id"     => [
                    "required",
                    "int",
                    Rule::exists((new ArticleModel())->getTable(), "id")
                ],
                "putTop" => self::intInValidateBuild(true, ArticleModel::PUT_TOP_TEXT)
            ]
        );
    }

    /**
     * 排序校验
     *
     * @return MessageBag|object
     * @throws ValidationException
     * @throws ContainerExceptionInterface
     * @throws NotFoundExceptionInterface
     */
    public static function sortValidate($params)
    {
        return self::getInstance()->validate(
            ["id", "sort"],
            $params,
            [
                "id"   => [
                    "required",
                    "int",
                    Rule::exists((new ArticleModel())->getTable(), "id")
                ],
                "sort" => "required|int|min:0"
            ]
        );
    }


    /**
     * 详情校验
     *
     * @return MessageBag|object
     * @throws ValidationException
     * @throws ContainerExceptionInterface
     * @throws NotFoundExceptionInterface
     */
    public static function apiDetailValidate($params)
    {
        return self::getInstance()->validate(
            ["id"],
            $params,
            [
                "id" => [
                    "required",
                    "int",
                    Rule::exists((new ArticleModel())->getTable(), "id")
                        ->where("status", ArticleModel::STATUS_ON)
                        ->whereIn("publishType", [ArticleModel::PUBLISH_TYPE_TIMING, ArticleModel::PUBLISH_TYPE_NOW])
                        ->where(function ($query) {
                            $query->where("effectiveTime", "<=", time());
                        })
                        ->where("isDeleted", ArticleModel::DELETE_NO)
                ]
            ]
        );
    }


    /**
     * @param array $params
     * @return MessageBag|object
     * @throws ContainerExceptionInterface
     * @throws NotFoundExceptionInterface
     * @throws ValidationException
     */
    public static function apiIndexListDetailValidate(array $params)
    {
        return self::getInstance()->validate(
            [
                "pageSize",
                "pageNum"
            ],
            $params,
            [
                "pageSize" => "required|int|min:1|max:100",
                "pageNum"  => "required|int|min:1"
            ]
        );
    }

    /**
     * @param array $params
     * @return MessageBag|object
     * @throws ContainerExceptionInterface
     * @throws NotFoundExceptionInterface
     * @throws ValidationException
     */
    public static function apiOperateListDetailValidate(array $params)
    {
        return self::getInstance()->validate(
            [
                "pageSize",
                "pageNum",
                "type",
                "title"
            ],
            $params,
            [
                "pageSize" => "required|int|min:1|max:100",
                "pageNum"  => "required|int|min:1",
                "type"     => self::intInValidateBuild(true, ArticleUserRelationModel::TYPE_TEXT),
                "title"    => "sometimes|string"
            ]
        );
    }


    /**
     * 视频校验列表
     *
     * @param array $params
     * @return MessageBag|object
     * @throws ContainerExceptionInterface
     * @throws NotFoundExceptionInterface
     * @throws ValidationException
     */
    public static function videoListValidate(array $params)
    {
        return self::getInstance()->validate(
            [
                "articleId",
                "categoryId",
                "pageSize",
                "pageNum",
            ],
            $params,
            [
                "articleId"  => [
                    "required",
                    "int",
                    Rule::exists((new ArticleModel())->getTable(), "id")
                        ->where("type", ArticleModel::TYPE_VIDEO)
                        ->where("isDeleted", 0)
                ],
                "categoryId" => [
                    "nullable",
                    "int",
                    Rule::exists((new ArticleCategoryModel())->getTable(), "id")->where("isDeleted", 0)
                ],
                "pageSize"   => Rule::when(
                    $params["categoryId"] ?? 0,
                    "",
                    "required|int|min:1|max:100"
                ),
                "pageNum"    => Rule::when(
                    $params["categoryId"] ?? 0,
                    "",
                    "required|int|min:1"
                ),
            ]
        );
    }

    /**
     * 分页内容校验
     *
     * @param array $params
     * @return MessageBag|object
     * @throws ContainerExceptionInterface
     * @throws NotFoundExceptionInterface
     * @throws ValidationException
     */
    public static function tabContentValidate(array $params)
    {
        $needPage = in_array(($params["type"] ?? null),
            [ArticleModel::INDEX_TAB_TYPE_0, ArticleModel::INDEX_TAB_TYPE_3]);

        return self::getInstance()->validate(
            ["type", "id", "pageNum", "pageSize"],
            $params,
            [
                "type"     => self::intInValidateBuild(true, ArticleModel::INDEX_TAB_TYPE_TEXT),
                "id"       => Rule::when(
                    ($params["type"] ?? null) == ArticleModel::INDEX_TAB_TYPE_0,
                    [
                        "required",
                        "int",
                        Rule::exists((new ArticleCategoryModel())->getTable(), "id")
                    ],
                    ""
                ),
                "pageSize" => Rule::when(
                    $needPage,
                    "required|int|min:1|max:100",
                    ""
                ),
                "pageNum"  => Rule::when(
                    $needPage,
                    "required|int|min:1",
                    ""
                ),
            ]
        );
    }

    /**
     * 置顶校验
     *
     * @param $params
     * @return MessageBag|object
     * @throws ContainerExceptionInterface
     * @throws NotFoundExceptionInterface
     * @throws ValidationException
     */
    public static function putHotTopValidate($params)
    {
        return self::getInstance()->validate(
            ["id", "putHotTop"],
            $params,
            [
                "id"        => [
                    "required",
                    "int",
                    Rule::exists((new ArticleModel())->getTable(), "id")
                        ->where("status", ArticleModel::STATUS_ON)
                        ->whereIn("publishType",
                            [
                                ArticleModel::PUBLISH_TYPE_TIMING,
                                ArticleModel::PUBLISH_TYPE_NOW
                            ])
                        ->where(function ($query) {
                            $query->where("effectiveTime", "<=", time());
                        })
                ],
                "putHotTop" => self::intInValidateBuild(true, ArticleModel::PUT_TOP_TEXT)
            ]
        );
    }
}
